import java.util.HashMap;
import java.util.Stack;

public class Environment {

	/*
	 * tree: decribes the parent child relationship env: decribes 0: primitive
	 * env -1: no more parent envTop: always points to the next available env
	 * marker int
	 */
	HashMap<Integer, Integer> tree = new HashMap<Integer, Integer>();
	HashMap<Tuple, EnvTreeNode> definition = new HashMap<Tuple, EnvTreeNode>();
	int envTop = 0;
	int currentEnv = 0;
	Stack<Integer> envLoaded = new Stack<Integer>();
	
	public Environment() {
		tree.put(0, -1);
		envTop++;
		definePEFunc(0);
	}

	public static void main(String[] args) {
		Environment env = new Environment();
		env.add(0, "x", "<INT:3>");
		int a = env.addEnv(0);
		env.add(a, "x", "<INT:4>");
		int b = env.addEnv(0);
		env.add(b, "y", "<INT:5>");
		System.out.println(env.lookup(a, "x"));
		System.out.println(env.lookup(b, "x"));
		System.out.println(env.lookup(b, "y"));
	}

	private void definePEFunc(int i) {
		definition.put(new Tuple(0, "<ID:Print>"), new PrintEnvObject());
		definition.put(new Tuple(0, "<ID:Order>"), new OrderPE());
		definition.put(new Tuple(0, "<ID:Stem>"), new StemPE());
		definition.put(new Tuple(0, "<ID:Stern>"), new SternPE());
		definition.put(new Tuple(0, "<ID:Conc>"), new ConcPE());
		definition.put(new Tuple(0, "<ID:Isinteger>"), new IsintegerPE());
		definition.put(new Tuple(0, "<ID:Isfunction>"), new IsfunctionPE());
		definition.put(new Tuple(0, "<ID:Istuple>"), new IstuplePE());
		definition.put(new Tuple(0, "<ID:Isdummy>"), new IsdummyPE());
		definition.put(new Tuple(0, "<ID:Isstring>"), new IsstringPE());
		definition.put(new Tuple(0, "<ID:Istruthvalue>"), new IstruthvaluePE());
		definition.put(new Tuple(0, "<ID:ItoS>"), new ItoSPE());
	}

	/*
	 * This method is used to open a new environment
	 */
	public int addEnv(int parent) {
		int newEnv = envTop;
		envTop++;
		tree.put(newEnv, parent);
		return newEnv;
	}

	/*
	 * This method is used to append definition to an environment
	 * Identifirs live in ENV in their true form
	 */
	public void add(int envNo, String x, String value) {
		Tuple key = new Tuple(envNo, x);
		definition.put(key, new EnvTreeNode(value));
	}

	public String lookup(int envNo, String object) {
		Tuple key = new Tuple(envNo, object);
		while (!definition.containsKey(key) && envNo != 0) {
			envNo = tree.get(envNo);
			key = new Tuple(envNo, object);
		}
		
		if (envNo == 0 && definition.containsKey(key)) {
			key = new Tuple(0, object);
			return definition.get(key).value;
		}else if (envNo != 0 && definition.containsKey(key)) {
			return definition.get(key).value;
		}else {
			System.out.println("lookup failed:"+ object + "lookup failed");
			System.exit(0);
			return null;
		}
	}

	public PEObject PELookup(String object) {
		Tuple key = new Tuple(0, object);
		if (definition.containsKey(key)) {
			return (PEObject) definition.get(key);
		} else {
			return null;
		}
	}

	@Override
	public String toString() {
		return "Environment [tree=" + tree + ", env=" + definition
				+ ", envTop=" + envTop + "]";
	}

}

class Tuple {
	private Integer i;
	private String s;

	@Override
	public String toString() {
		return "Tuple [i=" + i + ", s=" + s + "]";
	}

	public Tuple(Integer i, String s) {
		this.i = i;
		this.s = s;
	}

	@Override
	public int hashCode() {
		return i.hashCode() + 31 * s.hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (o == this)
			return true;
		if (o == null || !(o instanceof Tuple))
			return false;
		Tuple cp = Tuple.class.cast(o);
		return i.equals(cp.i) && s.equals(cp.s);
	}
}